﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Civilisation_code.PRessource;
using Civilisation_code.PJoueur;
using Civilisation_code.PInterfaces_Fabrique_Unite;
using System.ComponentModel;
using System.Collections;

namespace Civilisation_code.PCarte
{
    /// <summary>
    /// Classe contenant toutes les informations d'une case : nombre d'unité dessus, ville qui l'a annexée
    /// </summary>
    /// <remarks>
    /// Client dans le patron de conception Poids-Mouche
    /// </remarks>
    [Serializable]
    public class Case : INotifyPropertyChanged
    {
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string name)
        {
            if(PropertyChanged!=null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }


        /// <summary>
        /// Position de la case sur l'axe des abscisses
        /// </summary>
        public int _x { get; set; }

        /// <summary>
        /// Dit si la case doit être masquée
        /// </summary>
        private bool _brouillard;
        public bool Brouillard
        {
            get
            {
                return _brouillard;
            }
            set
            {
                this._brouillard = value;
                OnPropertyChanged("brouillard");
            }
        }
        /// <summary>
        /// Position de la case sur l'axe des ordonnées
        /// </summary>
        public int _y { get; set; }
        /// <summary>
        /// Ressource produite par la case
        /// </summary>
        public Ressource _ressource {get;set;}

        /// <summary>
        /// Liste des unités présentes sur la case
        /// </summary>
        private List<Unite> _unites;
        public List<Unite> Unites
        {
            get
            {
                return _unites;
            }
            set
            {
                _unites = value;
            }
        }
        /// <summary>
        /// Ville qui a annexée sur la case
        /// </summary>
        private Ville _ville_annexee;
        public Ville Ville_annexee {
            get
            {
                return _ville_annexee;
            }
            set
            {
                this._ville_annexee = value;
                OnPropertyChanged("ville_annexee");
            }
        }


        /// <summary>
        /// Valeur de la case (estimée par ses bonus)
        /// </summary>
        public int _valeur{get;set;}


        /// <summary>
        /// Ville qui est sur la case
        /// </summary>
        private Ville _ville;
        public Ville Ville
        {
            get
            {
                return _ville;
            }
            set
            {
                this._ville = value;
                OnPropertyChanged("ville");
            }

        }



        public void ajoutUnite(Unite u)
        {
            _unites.Add(u);
            OnPropertyChanged("ajout_unite");
        }
        /// <summary>
        /// Joueur possédant des unités dessus
        /// </summary>
        public Joueur _joueur {
            get
            {
                //Si il n'y a pas d'unités sur la case => pas de joueur
                if (_unites.Count.Equals(0))
                    return null;
                //Sinon on retourne le joueur
                return _unites[0]._joueur;
            }
        }

        /// <summary>
        /// Constructeur d'une instance de Case
        /// </summary>
        /// <param name="x">Position de la case sur l'axe des abscisses</param>
        /// <param name="y">Position de la case sur l'axe des ordonnées</param>
        /// <param name="r">Nom de la ressource</param>
        /// <param name="c">Carte où se trouve la case</param>
        /// <param name="bonus">Nom du bonus</param>
        public Case(int x, int y, string r, string bonus = "")
        {
            //Initialisation des attributs
            _x = x;
            _y = y;
            Brouillard = false;
            _unites = new List<Unite>();
            _ressource = Fabrique_Ressource.FABRIQUE_RESSOURCE.obtenirRessource(r,bonus);
            _ville = null;
            _ville_annexee = null;
            //_carte = c;
        }

        /// <summary>
        /// Liste des cases dans un rayon donné en paramètre
        /// </summary>
        /// <remarks>
        /// Méthode utile pour le brouillard de guerre
        /// </remarks>
        /// <param name="rayon">Rayon</param>
        /// <returns>Liste des cases dans un rayon donné en paramètre</returns>
        public List<Case> vueSur(int rayon)
        {
            List<Case> l = new List<Case>();
            //Parcours de toutes les cases dans un rayon donné
            for (int x = this._x - rayon; x <= this._x + rayon; x++)
            {
                for (int y = this._y - rayon; y <= this._y + rayon; y++)
                {
                    //Récupération de la case pour une coordonnée x:y
                    Case tmp = PPartie.Partie.INSTANCE._carte.getCase(x, y);

                    //Si la case n'est pas à null alors elle existe
                    //Permet de gérer les cas hors-carte
                    if (tmp != null)
                        l.Add(tmp);
                }
            }
            return l;
        }

        /// <summary>
        /// Calcule la distance entre cette case et la case envoyée en paramètre
        /// </summary>
        /// <param name="c">Case cible</param>
        /// <returns>Distance entre cette case et la case envoyée en paramètre</returns>
        public int calculerDistance(Case c)
        {
            return Math.Abs(_x - c._x) + Math.Abs(_y - c._y);
        }

        /// <summary>
        /// Renvoit la meilleure unité défensive située sur la case
        /// </summary>
        /// <returns>Meilleure unité défensive de la case</returns>
        public Unite meilleureUniteDefensive()
        {
            Unite meilleure_unite = null; 
            foreach (Unite unite in _unites)
            {
                if (meilleure_unite == null || unite.calculerPointDefense() > meilleure_unite.calculerPointDefense())
                    meilleure_unite = unite;
            }
            return meilleure_unite;
        }
		
        /// <summary>
        /// Permet de savoir si un directeur est situé sur la case
        /// </summary>
        /// <returns>Vrai si un directeur est situé sur la case
        ///          Faux sinon
        /// </returns>
		public bool contientDirecteur(){
            //Si le nombre de directeur est de zéro
			if(getNombreTypeUnite("Directeur") == 0)
			    return false;
            else
                return true;
		}

        /// <summary>
        /// Donne le nombre d'unités d'un type envoyé en paramètre présent sur la case
        /// </summary>
        /// <param name="type">Type de l'unité (Directeur, Enseignant ou Etudiant)</param>
        /// <returns>Nombre d'unités du type envoyé en paramètre</returns>
        public int getNombreTypeUnite(string type)
        {
            int nbre = 0;

            //Parcours de tous nos unités présents sur la case
            foreach(Unite unit in _unites){
                //Si classe de l'unité est une sous-classe du type
                if (unit.GetType().IsSubclassOf(Type.GetType("Civilisation_code.PInterfaces_Fabrique_Unite."+type)))
                    nbre++;
            }
            return nbre;
        }

        /// <summary>
        /// Retire une unité de la case
        /// </summary>
        /// <param name="unite"></param>
        public void retirerUnite(Unite unite)
        {
            Unites.Remove(unite);
            OnPropertyChanged("enleve_unite");
        }

        /// <summary>
        /// Donne les meilleurs cases environnantes
        /// </summary>
        /// <returns>Liste des cases triées par valeur</returns>
        public List<Case> meilleursCasesPourUnVillage()
        {
            List<Case> cases = new List<Case>();
            int rayon = 3;

            for (int i = _x - rayon; i <= _x + rayon; i++)
            {
                for (int j = _y - rayon; j <= _y + rayon; j++)
                {
                    if (i >= 0 && j >= 0 && i < PPartie.Partie.INSTANCE._carte._largeur && j < PPartie.Partie.INSTANCE._carte._hauteur)
                        if (PPartie.Partie.INSTANCE._carte.getCase(i, j)._ville == null)
                            cases.Add(PPartie.Partie.INSTANCE._carte.getCase(i, j));
                }
            }

            cases.Sort(ComparerCases);

            return cases;
        }

        /// <summary>
        /// Méthode de tri entre 2 cases
        /// </summary>
        /// <param name="a">case 1</param>
        /// <param name="b">case 2</param>
        /// <returns>l'ordre</returns>
        private static int ComparerCases(Case a, Case b)
        {
            return b._valeur.CompareTo(a._valeur);
        }

    }
}
